package server.core;

import server.*;
import javax.servlet.http.HttpServletRequest;

/*
StandardContext容器的标准映射器,负责根据请求匹配出符合要求的wrapper容器,
即根据请求匹配出要处理请求的servlet类,有四种匹配规则,为匹配到时返回null
 */
/*
2023/3/3 该类内容全部写完,后续直接使用,无需修改
by cyl
 */
public class StandardContextMapper implements Mapper {
    private StandardContext context = null;
    private String protocol = null;

    @Override
    public Container getContainer() {
        return context;
    }

    @Override
    public void setContainer(Container container) {
        if (!(container instanceof StandardContext))
            throw new IllegalArgumentException("This container is not a StandardContext");
        this.context = (StandardContext) container;
    }

    @Override
    public String getProtocol() {
        return protocol;
    }

    @Override
    public void setProtocol(String protocol) {
        this.protocol = protocol;
    }

    /*
    Servlet 虚拟路径匹配规则有以下 4 种：
    完全路径匹配:
        以/开始,不能包含通配符*,必须完全匹配	如/myServlet,/user/myServlet,/product/index.action
    目录匹配:
        以/字符开头,并以/*结尾的字符串。用于路径匹配 如/user/*, /*
    扩展名匹配:
        以通配符*.开头的字符串。用于扩展名匹配 *.do,*.action,*.jsp
    缺省匹配（默认匹配）:
        映射路径为/，表示这个 Servlet 为当前应用的缺省 Servlet 或默认 Servlet，默认处理无法匹配到虚拟路径的请求.
    Servlet 虚拟路径的匹配优先级顺序为：全路径匹配（精确匹配）> 目录匹配 > 扩展名匹配 > 缺省匹配（默认匹配）。
     */
    @Override
    public Container map(Request request, boolean update) {
        int debug = context.getDebug();
        HttpServletRequest hreq = (HttpServletRequest)request;

        String contextPath = hreq.getContextPath();
        String uri = ((HttpRequest)(request)).getDecodedRequestURI();
        //去掉应用名后的路径
        String path = uri.substring(contextPath.length());
        if (debug >= 1)
            context.log("Mapping contextPath='" + contextPath +
                    "' with requestURI='" + uri +
                    "' and relativeURI='" + path + "'");

        Wrapper wrapper = null;
        //匹配到servlet的路径,只有目录匹配的路径是配置路径去掉/*(如请求为/a/b/c,匹配路径为/a/*,则servletPath=/a),其他匹配方式都是与path相同
        String servletPath = path;
        //只有目录匹配的路径是path去掉servletPath之后的内容,(如请求为/a/b/c,匹配路径为/a/*,则pathInfo=/b/c)其他匹配方式都是null
        String pathInfo = null;
        String wrapperName = null;

        //精确匹配
        if(wrapper == null) {
            if (debug >= 2)
                context.log("尝试精确匹配");
            if (!"/".equals(path))
                wrapperName = context.findServletMapping(path);
            if (wrapperName != null)
                wrapper = (Wrapper)context.findChild(wrapperName);
            if (wrapper != null) {
                //找到wrapper了
                servletPath = path;
                pathInfo = null;
            }
        }
        //目录匹配
        if (wrapper == null) {
            if (debug >= 2)
                context.log("尝试目录匹配");
            servletPath = path;
            while (true) {
                wrapperName = context.findServletMapping(servletPath + "/*");
                if (wrapperName != null)
                    wrapper = (Wrapper)context.findChild(wrapperName);
                if (wrapper != null) {
                    pathInfo = path.substring(servletPath.length());
                    if (pathInfo.length() == 0)
                        pathInfo = null;
                    break;
                }
                int index = servletPath.lastIndexOf('/');
                if (index < 0)
                    break;
                servletPath = servletPath.substring(0, index);
            }
        }
        //扩展名匹配
        if (wrapper == null) {
            if (debug >= 2)
                context.log("尝试扩展名匹配");
            int slashIndex = path.lastIndexOf('/');
            if (slashIndex >= 0) {
                int pointIndex = path.lastIndexOf('.');
                if (pointIndex > slashIndex) {
                    String pattern = "*" + path.substring(pointIndex);
                    wrapperName = context.findServletMapping(pattern);
                    if (wrapperName != null)
                        wrapper = (Wrapper)context.findChild(wrapperName);
                    if (wrapper != null) {
                        servletPath = path;
                        pathInfo = null;
                    }
                }
            }
        }
        //缺省匹配(只要配置了路径为/的servlet,所有之前没有匹配到的请求都由该servlet处理)
        if (wrapper == null) {
            if (debug > 2)
                context.log("尝试缺省匹配");
            wrapperName = context.findServletMapping("/");
            if (wrapperName != null)
                wrapper = (Wrapper)context.findChild(wrapperName);
            if (wrapper != null) {
                servletPath = path;
                pathInfo = null;
            }
        }

        if (debug >= 1 && wrapper != null)
            context.log(" Mapped to servlet '" + wrapper.getName() +
                    "' with servlet path '" + servletPath +
                    "' and path info '" + pathInfo +
                    "' and update=" + update);
        //设置两个路径
        if (update) {
            ((HttpRequest)request).setServletPath(servletPath);
            ((HttpRequest) request).setPathInfo(pathInfo);
        }
        return wrapper;
    }
}
